package com.market.plus.core.dao.jdbc;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementCreatorFactory;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterUtils;
import org.springframework.jdbc.core.namedparam.ParsedSql;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Service;

import com.market.plus.core.dao.NewsDb;
import com.market.plus.core.dao.jdbc.rowmapper.NewsRowMapper;
import com.market.plus.core.domain.News;

@Service("jdbcNewsDao")
public class JdbcNewsDao extends JdbcGenericDao implements NewsDb {

   public static final int DEFAULT_CACHE_LIMIT = 256;


   private volatile int cacheLimit = DEFAULT_CACHE_LIMIT;

   private final Map<String, ParsedSql> parsedSqlCache =
         new LinkedHashMap<String, ParsedSql> (DEFAULT_CACHE_LIMIT, 0.75f, true) {
            private static final long serialVersionUID = 1L;

            @Override
            protected boolean removeEldestEntry(Map.Entry<String, ParsedSql> eldest) {
               return size() > getCacheLimit();
            }
         };

   @Autowired
   private SQLCommand sqlCommand;

   @Autowired
   public JdbcNewsDao(final DataSource dataSource) {
      super.setDataSource(dataSource);
   }

   public List<News> getNews(Long participantId) {
      String sql = sqlCommand.getSelectCommonNewsByParticipantId();
      Object[] args = new Object[] { participantId.longValue() };
      List<News> news = this.getJdbcTemplate().query(sql, args, new NewsRowMapper());
      return news;
   }

   public List<News> getPrivateNews(Long participantId, List<Long> friendIds) {
      String sql = sqlCommand.getSelectPrivateNewsByParticipantId();
      MapSqlParameterSource paramSource = new MapSqlParameterSource();
      paramSource.addValue("partId", participantId);
      paramSource.addValue("friendIds", friendIds);
      List<News> news = this.getJdbcTemplate().query(getPreparedStatementCreator(sql, paramSource), new NewsRowMapper());
      return news;
   }

   public void setCacheLimit(int cacheLimit) {
      this.cacheLimit = cacheLimit;
   }

   public int getCacheLimit() {
      return this.cacheLimit;
   }
   
   protected PreparedStatementCreator getPreparedStatementCreator(String sql, SqlParameterSource paramSource) {
      ParsedSql parsedSql = getParsedSql(sql);
      String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
      Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null);
      int[] paramTypes = NamedParameterUtils.buildSqlTypeArray(parsedSql, paramSource);
      PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(sqlToUse, paramTypes);
      return pscf.newPreparedStatementCreator(params);
   }

   protected ParsedSql getParsedSql(String sql) {
      if (getCacheLimit() <= 0) {
         return NamedParameterUtils.parseSqlStatement(sql);
      }
      synchronized (this.parsedSqlCache) {
         ParsedSql parsedSql = this.parsedSqlCache.get(sql);
         if (parsedSql == null) {
            parsedSql = NamedParameterUtils.parseSqlStatement(sql);
            this.parsedSqlCache.put(sql, parsedSql);
         }
         return parsedSql;
      }
   }

}
